namespace Plugins.Engine
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using Plugins.Interfaces;
    using Plugins.Library;
    using Plugins.Serializer;

    [Serializable]
    public class PluginPropertyContainer : IPluginPropertyContainer, IPluginContainer<IPluginProperty>, IPluginConstructible<IPluginPropertyContainer>, IPluginEvent<IPluginProperty>
    {
        private static IPluginStringSerializer<PluginPropertyContainer> _xmlSerializer = new PluginUnicodeStringSerializer<PluginPropertyContainer>();
        private static IPluginRuntimeFormatter _formatter = new XmlFormatter();

        public static IPluginStringSerializer<PluginPropertyContainer> XmlSerializer { get { return _xmlSerializer; } }
        public static IPluginRuntimeFormatter Formatter { get { return _formatter; } }

        private Dictionary<string, List<PluginPriorityItem<Action<IPluginProperty>>>> _actionContainer = new Dictionary<string, List<PluginPriorityItem<Action<IPluginProperty>>>>();

        public PluginPropertyContainer()
        {
            this.AddItemAddedEventHandler(this.PropertyContainer_ItemAdded);
            this.AddItemChangedEventHandler(this.PropertyContainer_ItemChanged);
            this.AddItemRemovedEventHandler(this.PropertyContainer_ItemRemoved);
        }

        private void PropertyContainer_ItemAdded(object obj, PluginPropertyEventArgs property)
        {
            this.PropertyChanged(property.Property);
        }
        private void PropertyContainer_ItemChanged(object obj, PluginPropertyEventArgs property)
        {
            this.PropertyChanged(property.Property);
        }
        private void PropertyContainer_ItemRemoved(object obj, PluginPropertyEventArgs property)
        {
            this.PropertyChanged(property.Property);
        }

        private void PropertyChanged(IPluginProperty property)
        {
            if (property != null && property.Name != null)
            {
                if (_actionContainer.ContainsKey(property.Name))
                {
                    List<PluginPriorityItem<Action<IPluginProperty>>> actionList = _actionContainer[property.Name];
                    if (actionList != null)
                    {
                        for (int i = actionList.Count; i > 0; i--)
                        {
                            PluginPriorityItem<Action<IPluginProperty>> item = actionList[i - 1];
                            item.Item(property);
                        }
                    }
                }
            }
        }

        public void UpdateProperties()
        {
            foreach (PluginPropertyValue property in this._dictionary)
            {
                this.PropertyChanged(property);
            }
        }

        public void UpdateProperty(string name)
        {
            PluginPropertyValue property = this._dictionary[name] as PluginPropertyValue;
            if (property != null)
            {
                this.PropertyChanged(property);
            }
        }

        public void AddEvent(string propertyName, Action<IPluginProperty> propertyHandler, int priority)
        {
            if (propertyName != null)
            {
                List<PluginPriorityItem<Action<IPluginProperty>>> actionList;
                if (!_actionContainer.ContainsKey(propertyName))
                {
                    actionList = new List<PluginPriorityItem<Action<IPluginProperty>>>();
                    _actionContainer.Add(propertyName, actionList);
                }
                else
                {
                    actionList = _actionContainer[propertyName];
                }
                PluginPriorityItem<Action<IPluginProperty>> item = new PluginPriorityItem<Action<IPluginProperty>>(priority, propertyHandler);
                if (!actionList.Contains(item))
                {
                    actionList.Add(item);
                    actionList.Sort();
                }
            }
        }
        public void AddEvent(string propertyName, Action<IPluginProperty> propertyHandler)
        {
            if (propertyName != null)
            {
                List<PluginPriorityItem<Action<IPluginProperty>>> actionList;
                if (!_actionContainer.ContainsKey(propertyName))
                {
                    actionList = new List<PluginPriorityItem<Action<IPluginProperty>>>();
                    _actionContainer.Add(propertyName, actionList);
                }
                else
                {
                    actionList = _actionContainer[propertyName];
                }
                PluginPriorityItem<Action<IPluginProperty>> item = new PluginPriorityItem<Action<IPluginProperty>>(0, propertyHandler);
                if (!actionList.Contains(item))
                {
                    actionList.Add(item);
                    actionList.Sort();
                }
            }
        }
        public void RemoveEvent(string propertyName, Action<IPluginProperty> propertyHandler)
        {
            if (propertyName != null)
            {
                List<PluginPriorityItem<Action<IPluginProperty>>> actionList;
                if (_actionContainer.ContainsKey(propertyName))
                {
                    actionList = _actionContainer[propertyName];
                    actionList.Remove(new PluginPriorityItem<Action<IPluginProperty>>(0, propertyHandler));
                    if (actionList.Count == 0)
                    {
                        _actionContainer.Remove(propertyName);
                    }
                }
            }
        }
        public void RemoveEvent(string propertyName, Action<IPluginProperty> propertyHandler, int priority)
        {
            if (propertyName != null)
            {
                List<PluginPriorityItem<Action<IPluginProperty>>> actionList;
                if (_actionContainer.ContainsKey(propertyName))
                {
                    actionList = _actionContainer[propertyName];
                    actionList.Remove(new PluginPriorityItem<Action<IPluginProperty>>(priority, propertyHandler));
                    if (actionList.Count == 0)
                    {
                        _actionContainer.Remove(propertyName);
                    }
                }
            }
        }

        private PluginPropertyCollection<PluginPropertyValue> _dictionary = new PluginPropertyCollection<PluginPropertyValue>();

        private event EventHandler<PluginPropertyEventArgs> ItemAdded;
        private event EventHandler<PluginPropertyEventArgs> ItemChanged;
        private event EventHandler<PluginPropertyEventArgs> ItemRemoved;

        protected void AddItemAddedEventHandler(EventHandler<PluginPropertyEventArgs> eventHandler)
        {
            ItemAdded += eventHandler;
        }
        protected void AddItemChangedEventHandler(EventHandler<PluginPropertyEventArgs> eventHandler)
        {
            ItemChanged += eventHandler;
        }
        protected void AddItemRemovedEventHandler(EventHandler<PluginPropertyEventArgs> eventHandler)
        {
            ItemRemoved += eventHandler;
        }
        protected void RemoveItemAddedEventHandler(EventHandler<PluginPropertyEventArgs> eventHandler)
        {
            ItemAdded -= eventHandler;
        }
        protected void RemoveItemChangedEventHandler(EventHandler<PluginPropertyEventArgs> eventHandler)
        {
            ItemChanged -= eventHandler;
        }
        protected void RemoveItemRemovedEventHandler(EventHandler<PluginPropertyEventArgs> eventHandler)
        {
            ItemRemoved -= eventHandler;
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public bool Add(IPluginProperty value)
        {
            try
            {
                PluginPropertyValue property = value as PluginPropertyValue;
                if (property != null)
                {
                    this._dictionary.Add(property);
                    if (this.ItemAdded != null)
                    {
                        this.ItemAdded(this, new PluginPropertyEventArgs(value));
                    }
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
                return false;
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public bool Add(string propertyName)
        {
            try
            {
                if (!Contains(propertyName))
                {
                    PluginPropertyValue<object> property = this.GetProperty<object>(propertyName, null);
                    if (property != null)
                    {
                        this._dictionary.Add(property);
                        if (this.ItemAdded != null)
                        {
                            this.ItemAdded(this, new PluginPropertyEventArgs(property));
                        }
                        return true;
                    }
                }
                return false;
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
                return false;
            }
        }

        public bool Contains(string propertyName)
        {
            return _dictionary.ContainsProperty(propertyName);
        }

        [SuppressMessage("Microsoft.Naming", "CA1725")]
        public void Initialize(IPluginPropertyContainer value)
        {
            PluginPropertyContainer obj = value as PluginPropertyContainer;
            if (obj != null)
            {
                this._dictionary = obj._dictionary;
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public void Clear()
        {
            try
            {
                if (this._dictionary.Count > 0)
                {
                    PluginPropertyCollection<PluginPropertyValue> tmp = new PluginPropertyCollection<PluginPropertyValue>(this._dictionary);
                    this._dictionary.Clear();
                    foreach (PluginPropertyValue property in tmp)
                    {
                        if (this.ItemRemoved != null)
                        {
                            this.ItemRemoved(this, new PluginPropertyEventArgs(property));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
            }
        }

        private PluginPropertyValue<T> GetProperty<T>(string propertyName)
        {
            return this.GetProperty<T>(propertyName, default(T));
        }
        private PluginPropertyValue<T> GetProperty<T>(string propertyName, T defaultValue)
        {
            return this.GetProperty<T>(propertyName, defaultValue, PluginPropertyConverter<T>.Default);
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        private PluginPropertyValue<T> GetProperty<T>(string propertyName, T defaultValue, IPluginPropertyConverter<T> converter)
        {
            try
            {
                PluginPropertyValue property = this._dictionary[propertyName] as PluginPropertyValue;
                if (property != null)
                {
                    return new PluginPropertyValue<T>(property, defaultValue, converter);
                }
                return null;
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
            }
            return null;
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public T GetValue<T>(string propertyName)
        {
            return this.GetValue<T>(propertyName, default(T));
        }
        [SuppressMessage("Microsoft.Design", "CA1004")]
        public T GetValue<T>(string propertyName, T defaultValue)
        {
            return this.GetValue<T>(propertyName, defaultValue, PluginPropertyConverter<T>.Default);
        }
        [SuppressMessage("Microsoft.Design", "CA1004")]
        public T GetValue<T>(string propertyName, T defaultValue, IPluginPropertyConverter<T> converter)
        {
            if (converter != null)
            {
                IPluginProperty<T> option = this.GetProperty<T>(propertyName, defaultValue, converter);
                if (option != null)
                {
                    T result = option.GetValue();
                    if (!object.Equals(result, defaultValue))
                    {
                        return result;
                    }
                }
            }
            return defaultValue;
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public bool Remove(IPluginProperty value)
        {
            try
            {
                PluginPropertyValue property = value as PluginPropertyValue;
                if (property != null)
                {
                    this._dictionary.Remove(property);
                    if (this.ItemRemoved != null)
                    {
                        this.ItemRemoved(this, new PluginPropertyEventArgs(value));
                    }
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
                return false;
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        public bool Remove(string propertyName)
        {
            try
            {
                if (Contains(propertyName))
                {
                    PluginPropertyValue<object> property = this.GetProperty<object>(propertyName, null);
                    if (property != null)
                    {
                        this._dictionary.Remove(property);
                        if (this.ItemRemoved != null)
                        {
                            this.ItemRemoved(this, new PluginPropertyEventArgs(property));
                        }
                    }
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                PluginLog.WriteErrorSync(ex);
                return false;
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1004")]
        public bool SetValue<T>(string propertyName, T value)
        {
            return this.SetValue<T>(propertyName, value, default(T));
        }
        [SuppressMessage("Microsoft.Design", "CA1004")]
        public bool SetValue<T>(string propertyName, T value, T defaultValue)
        {
            return this.SetValue<T>(propertyName, value, defaultValue, PluginPropertyConverter<T>.Default);
        }
        [SuppressMessage("Microsoft.Design", "CA1004")]
        public bool SetValue<T>(string propertyName, T value, T defaultValue, IPluginPropertyConverter<T> converter)
        {
            PluginPropertyValue<T> property = this.GetProperty<T>(propertyName, defaultValue, converter);
            if (property != null)
            {
                if (!property.Equals(value))
                {
                    property.SetValue(value);
                    if (this.ItemChanged != null)
                    {
                        this.ItemChanged(this, new PluginPropertyEventArgs(property));
                    }
                }
                return true;
            }
            else
            {
                this._dictionary.Add(new PluginPropertyValue(/*typeof(PluginPropertyValue<T>), */propertyName, PluginPropertyConverter.ConvertToString<T>(converter, defaultValue)));
            }
            return false;
        }

        bool IPluginContainer<IPluginProperty>.Contains(IPluginProperty value)
        {
            PluginPropertyValue property = value as PluginPropertyValue;
            if (property != null)
            {
                return this._dictionary.Contains(property);
            }
            return false;
        }
        
        //protected bool Contains(IPluginProperty item)
        //{
        //    return ((IPluginContainer<IPluginProperty>)this).Contains(item);
        //} 

        public int Count
        {
            get
            {
                return this._dictionary.Count;
            }
        }

        public PluginPropertyCollection<PluginPropertyValue> Properties
        {
            get
            {
                return this._dictionary;
            }
            set
            {
                this._dictionary = value;
            }
        }
        
        private class XmlFormatter : PluginTypeSerializer<PluginPropertyContainer>
        {
            public override void AddOverrides(IPluginTypeFormatter formatter)
            {
                AddType(formatter, "properties");
                AddArray(formatter, "Properties", "properties");
                AddArrayItem(formatter, "Properties", "item", typeof(PluginPropertyValue));
                PluginPropertyValue.PluginPropertyValueXmlFormatter.AddOverrides(formatter);
            }
        }
    }
}

